package com.veetao.api.filter;

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.stereotype.Component;

import com.veetao.api.consts.HttpConstants;
import com.veetao.api.model.ApiResult;
import com.veetao.api.model.ApiResultCode;
import com.veetao.api.utils.ConfigUtils;
import com.veetao.api.utils.McpUtils;
import com.veetao.api.utils.NumberUtil;
import com.veetao.api.utils.RequestUtils;

@Component
public class UserActionFrequencyChecker extends ApiRequestChecker {
	private static final Log logger = LogFactory.getLog("useraction.frequency");

	private static final Map<String, RequestChecker> mapRequestCheckers = new HashMap<String, RequestChecker>();
	/** 一个用户每分钟请求数 */
	private static final int requestNumPerMinutes = ConfigUtils.requestNumPerMinutes();

	/** 两次阻塞日志间的最小时间 */
	private static final int blockLogPeriodInSecond = 30;// 半分钟
	private static final int blockLogPeriodInMS = blockLogPeriodInSecond * 1000;


	public UserActionFrequencyChecker() {
		super();
		this.setOrder(5);
	}

	@Override
	public ApiResult check(HttpServletRequest request) {
		String userId = RequestUtils.get(request, HttpConstants.PARAM_UID);
		if(!NumberUtil.isNumber(userId) || userId.equals("0")) {
			// 非登录用户，不检测,直接返回
			return SUCC_RESULT;
		}
		
		RequestChecker checker = mapRequestCheckers.get(userId);
		if (null == checker) {
			checker = new RequestChecker();
			mapRequestCheckers.put(userId, checker);
			return SUCC_RESULT;
		}
		long interval = System.currentTimeMillis() - checker.getTime();
		if (interval < blockLogPeriodInMS) {
			// 在一个检测周期内
			if (checker.getRequestCount() >= requestNumPerMinutes) {
				// 请求次数达到阈值
				String methodName = McpUtils.getCmdMethodFromURI(request.getRequestURI());
				logger.error("" + userId + " has " + requestNumPerMinutes
							+ " request \n Exception:userId: " + userId + " has " + requestNumPerMinutes
							+ " request per minutes.method:" + methodName);
				
				if (!ConfigUtils.getRequestFastMode()) {
					// 没有显示设置快模式，警告用户操作太快
					
					startNewInterval(checker,(int) (interval/1000));
					logger.info(String.format("[%s] checker start time: %s, checker init count： %s", userId, checker.periodStartTime, checker.requestCount));
					
					return new ApiResult(ApiResultCode.E_SYS_REQUEST_TOO_FAST);
				}
				checker.resetStatus();
			} else {
				checker.increaseRequestCount();
			}
		} else {
			checker.resetStatus();
		}

		return SUCC_RESULT;
	
	}
	
	
	private static void startNewInterval(RequestChecker checker, int intervalInSecond) {
//		int requestCount = checker.getRequestCount();
//		float requestPerSecond = (1.0f*requestCount)/intervalInSecond;
//		int secondToIntervalEnd = blockLogPeriodInSecond - intervalInSecond;
		int lastInitCount = checker.getLastInitRequestCount();
		int thisInitCount = (int) (lastInitCount * 1.2f);
		if(lastInitCount == 0 || thisInitCount >= requestNumPerMinutes) {
			thisInitCount = requestNumPerMinutes/2;
		}
		checker.resetStatus();
		checker.setRequestCount(thisInitCount);
		checker.setLastInitRequestCount(thisInitCount);
	}
	
	
	public static void main(String args[]) {
		RequestChecker checker = new RequestChecker();
		checker.periodStartTime = System.currentTimeMillis() - 50000;
		checker.setRequestCount(60);

		startNewInterval(checker, 40);
		
		System.out.println(checker.periodStartTime);
		System.out.println(checker.requestCount);
	}


	static class RequestChecker {

	    private long periodStartTime;

	    private int requestCount;
	    
	    private int lastInitRequestCount;
	    
	    public RequestChecker() {
	    	
	    }

	    public int getRequestCount() {
	        return requestCount;
	    }

	    public void increaseRequestCount() {
	        this.requestCount = requestCount + 1;
	    }
	    public void increaseRequestCount(int by) {
	        this.requestCount = requestCount + by;
	    }
	    public long getTime() {
	        return periodStartTime;
	    }

	    public void resetStatus() {
	        this.periodStartTime = System.currentTimeMillis();
	        this.requestCount = 0;
	    }

		public void setRequestCount(int requestCount) {
			this.requestCount = requestCount;
		}

		public int getLastInitRequestCount() {
			return lastInitRequestCount;
		}

		public void setLastInitRequestCount(int lastInitRequestCount) {
			this.lastInitRequestCount = lastInitRequestCount;
		}

	}

}
